package cn.workreport.util;

import cn.hutool.core.util.StrUtil;
import cn.workreport.modules.common.annotations.ExcelTitle;
import lombok.extern.log4j.Log4j2;
import org.apache.poi.ss.usermodel.*;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 描述【】
 *
 *@author lihuanyao
 *@time 2021/11/26 22:25
 *
 */
@Log4j2
public class PoiUtil {

    public static void tableContents( Sheet sheet,int beginRow, List resultMap,Class tClass) {
        if (resultMap == null || resultMap.isEmpty()||tClass == null){
            return ;
        }

        if (resultMap.get(0).getClass() != tClass){
            throw new RuntimeException("数据类型与模本类型不一致！");
        }
        // 判断开始行是否比最后一行要大
        int lastRowNum = sheet.getLastRowNum();
        if (beginRow > lastRowNum){
            throw  new RuntimeException("数据越界！");
        }
        // 获取需要填充的表头数据
        Map<String,String> title = getTitle(tClass);
        if (title == null || title.isEmpty()){
            throw new RuntimeException("模版类型配置错误！");
        }
        //表格行是由0开始计算的，所以输入的序号需要减一处理方便参数输入减少人为计算
        beginRow -= 1;
        // 获取导出表头坐标信息
        Row row = sheet.getRow(beginRow - 1);
        Map<Integer,String> titleInfo = new HashMap<>();
        for (Cell cell : row) {
            if (CellType.STRING == cell.getCellTypeEnum()){
                String stringCellValue = cell.getStringCellValue();
                // 保存填充行的上一行标题行的表头信息，用于填充数据定位 titleInfo（坐标信息，表头名称）
                if (StrUtil.isNotBlank(stringCellValue) && title.containsKey(stringCellValue.trim())){
                    titleInfo.put(cell.getColumnIndex(),title.get(stringCellValue.trim()));
                }
            }
        }

        /**
         *  循环填充
         */
        Map<Integer,CellStyle> rowStyleMap = new HashMap<>();
        if (titleInfo != null && !titleInfo.isEmpty()) {
            // 保存填充行的样式数据 到 rowStyleMap（表格列坐标，单元格样式信息）
            Iterator<Cell> cellIterator = sheet.getRow(beginRow).cellIterator();
           while (cellIterator.hasNext()){
               Cell next = cellIterator.next();
               rowStyleMap.put(next.getColumnIndex(),next.getCellStyle());
           }
            // 将表格填充行以下部分移动到填充数据长度以下
            if (resultMap.size() > 1) {
                sheet.shiftRows(beginRow + 1, sheet.getLastRowNum(), resultMap.size() - 1);
            }
            for (int i = 0; i < resultMap.size(); i++) {
                Row row1 = sheet.createRow(beginRow + i );
                // 复制行样式到新创建的行
                copyCellStyle(row1,rowStyleMap);
                // 将数据填到单元格，当前只填充字符串数据，其他数据类型后期补充
                for (Map.Entry<Integer, String> entry : titleInfo.entrySet()) {
                    Object value = getEntityValueByName(entry.getValue(), resultMap.get(i), tClass);
                    Cell cell = row1.getCell(entry.getKey());
                    setCellValue(cell,value);
                }
            }
        }
    }

    /**
     *  单元格样式复制
     * @param row 行信息
     * @param rowStyleMap 行样式信息
     */
  private static void copyCellStyle(Row row, Map<Integer,CellStyle> rowStyleMap){
        if (row == null || rowStyleMap == null){
            return;
        }
      rowStyleMap.entrySet().forEach(item->{
          Cell cell = row.createCell(item.getKey());
          cell.setCellStyle(item.getValue());
      });

    }

    /**
     *  填充单元格数据 需要进行类型判定
     * @param cell 单元格
     * @param obj 填充数据
     */
    private static void setCellValue(Cell cell,Object obj){
       if (obj instanceof String){
           cell.setCellValue((String)obj);
       }
    }

    /**
     *  获取填充表头信息 (可以添加其他样式信息，如可以作为日期格式填充)
     * @param tClass 导出实体class
     * @return
     */
    private static Map<String,String> getTitle(Class tClass){
        Map<String,String> map = new HashMap<>();
        for (Field field : tClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(ExcelTitle.class)){
                ExcelTitle annotation = field.getAnnotation(ExcelTitle.class);
                String value = annotation.value();
                if (StrUtil.isNotBlank(value)) {
                    map.put(value, field.getName());
                }
            }
        }
        return map;
    }


    /**
     * 通过实体名获取实体对应值
     *
     * @param name
     * @return
     */
    private static Object getEntityValueByName(String name,Object obj,Class tClass) {
        if (StrUtil.isBlank(name) || tClass == null) {
            return null;
        }
        try {
            PropertyDescriptor pd = new PropertyDescriptor(name, tClass);
            Method getMethod = pd.getReadMethod();
            return getMethod.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("PoiUtil ==> getEntityValueByName 方法，获取" + name + "实体值出错！");
        }
        return null;
    }

}


